﻿namespace TestTool.HttpTransport
{
    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.ServiceModel;
    using System.Text;
    using System.Threading;
    using TestTool.HttpTransport.Interfaces;
    using TestTool.HttpTransport.Interfaces.Exceptions;
    using TestTool.HttpTransport.Internals.Http;

    public class HttpClient
    {
        private Uri _address;
        private ICredentialsProvider _credentialsProvider;
        private HttpPacket _digestAuthChallenge;
        private IExecutionController _executionController;
        private RequestNetworkStream _networkStream;
        private int _timeout;

        public HttpClient(string address, int timeout) : this(address, timeout, null, null)
        {
        }

        public HttpClient(string address, int timeout, IExecutionController controller, ICredentialsProvider credentialsProvider)
        {
            this._address = new Uri(address);
            this._timeout = timeout;
            this._executionController = controller;
            this._credentialsProvider = credentialsProvider;
        }

        private byte[] CreateMessageBytes(string request)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(request);
            string username = string.Empty;
            string password = string.Empty;
            if ((this._credentialsProvider != null) && (this._digestAuthChallenge != null))
            {
                username = this._credentialsProvider.Username;
                password = this._credentialsProvider.Password;
            }
            byte[] sourceArray = HttpHelper.CreateHttpHeaders((long) bytes.Length, this._address.AbsolutePath, this._address.Host, this._digestAuthChallenge, username, password, null);
            byte[] destinationArray = new byte[sourceArray.Length + bytes.Length];
            Array.Copy(sourceArray, destinationArray, sourceArray.Length);
            Array.Copy(bytes, 0, destinationArray, sourceArray.Length, bytes.Length);
            return destinationArray;
        }

        private void GetResponse(MemoryStream responseStream, out HttpPacket header, int readTimeout)
        {
            int count = 0;
            byte[] buffer = new byte[0x800];
            bool flag = false;
            int millisecondsTimeout = readTimeout;
            DateTime now = DateTime.Now;
            do
            {
                WaitHandle[] handleArray;
                IAsyncResult result = this._networkStream.BeginRead(buffer, 0, buffer.Length);
                if ((this._executionController != null) && (this._executionController.StopEvent != null))
                {
                    handleArray = new WaitHandle[] { result.AsyncWaitHandle, this._executionController.StopEvent };
                }
                else
                {
                    handleArray = new WaitHandle[] { result.AsyncWaitHandle };
                }
                switch (WaitHandle.WaitAny(handleArray, millisecondsTimeout))
                {
                    case 0x102:
                        this._networkStream.Close();
                        throw new TestTool.HttpTransport.Interfaces.Exceptions.TimeoutException("The HTTP request has exceeded the allotted timeout");

                    case 1:
                        this._networkStream.Close();
                        this._executionController.ReportStop();
                        break;
                }
                count = this._networkStream.EndRead(result);
                DateTime time2 = DateTime.Now;
                responseStream.Write(buffer, 0, count);
                TimeSpan span = (TimeSpan) (time2 - now);
                int totalMilliseconds = (int) span.TotalMilliseconds;
                try
                {
                    flag = HttpHelper.ContinueReading(responseStream, out header);
                }
                catch (Exception exception)
                {
                    this._networkStream.Close();
                    throw new Exception("An error occurred while parsing HTTP packet", exception);
                }
                millisecondsTimeout = readTimeout - totalMilliseconds;
                if (millisecondsTimeout < 0)
                {
                    this._networkStream.Close();
                    throw new IOException("The HTTP request has exceeded the allotted timeout");
                }
            }
            while (this._networkStream.DataAvailable || flag);
        }

        public string SendSoapMessage(string request)
        {
            byte[] buffer = this.CreateMessageBytes(request);
            this._networkStream = new RequestNetworkStream(new EndpointAddress(this._address.OriginalString));
            this._networkStream.Connect();
            this._networkStream.Write(buffer, 0, buffer.Length);
            MemoryStream responseStream = new MemoryStream();
            int readTimeout = this._timeout;
            HttpPacket header = null;
            do
            {
                responseStream = new MemoryStream();
                this.GetResponse(responseStream, out header, readTimeout);
                int statusCode = header.StatusCode;
            }
            while (header.StatusCode == 100);
            this._networkStream.Close();
            bool flag = false;
            if (this._credentialsProvider != null)
            {
                flag = this._credentialsProvider.Security == Security.Digest;
            }
            if (header.StatusCode == 0x191)
            {
                if (!flag)
                {
                    this._networkStream.Close();
                    throw new AccessDeniedException("Access denied (HTTP status 401 received)");
                }
                this._networkStream.Close();
                this._networkStream.EnsureOpen(new EndpointAddress(this._address.OriginalString));
                this._digestAuthChallenge = header;
                byte[] buffer2 = this.CreateMessageBytes(request);
                this._networkStream.Write(buffer2, 0, buffer2.Length);
                do
                {
                    responseStream = new MemoryStream();
                    this.GetResponse(responseStream, out header, readTimeout);
                }
                while (header.StatusCode == 100);
                if (header.StatusCode == 0x191)
                {
                    this._networkStream.Close();
                    throw new AccessDeniedException("Digest authentication FAILED (HTTP status 401 received)");
                }
            }
            this._networkStream.Close();
            int num2 = ((int) responseStream.Length) - header.BodyOffset;
            if (header.ContentLength < num2)
            {
                throw new HttpProtocolException(string.Format("An error occurred while receiving packet. Expected length: {0}, received: {1}", header.ContentLength, num2));
            }
            string formattedMessage = HttpHelper.GetFormattedMessage(responseStream.GetBuffer(), header.BodyOffset);
            responseStream.Close();
            return formattedMessage;
        }
    }
}

